﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WC3Game
{
    public class Player
    {
        public Player(Game game)
        {
            Parent = game;
        }

        public Player()
        {
        }

        public void SetGame(Game game)
        {
            Parent = game;
        }

        public Game Parent = null;

        Items items = new Items();
        Heroes heroes = new Heroes();

        public Int32 ID;

        public Boolean IsSelf = false;

        String name;

        public String Name
        {
            get { return name; }
            set { name = value; }
        }

        public String HeroID = String.Empty;
        public String HeroName
        {
            get
            {
                return heroes.HeroCodes[HeroID];
            }
        }

        List<SubItem> children = new List<SubItem>();

        public List<SubItem> SubItems
        {
            get { return children; }
            set { children = value; }
        }

        public List<Kill> kills = new List<Kill>();
        public List<Kill> deaths = new List<Kill>();

        public List<String> ItemList
        {
            get
            {
                List<String> itemList = new List<string>();
                foreach (String itemCode in Items)
                {
                    if (itemCode == String.Empty)
                        continue;

                    String item = String.Format("Unknown Item: {0}", itemCode);
                    try
                    {
                        item = items.ItemCodes[itemCode];
                    }
                    catch (KeyNotFoundException)
                    {
                    }

                    itemList.Add(item);
                }

                return itemList;
            }
        }

        public Int32 CurrentGold { get; set; }

        public Int32 KillCount
        {
            get
            {
                int killCount =
                    (from kill in kills
                     where kill.Type == Kill.KillType.Hero
                     select kill).Count();

                return killCount;
            }
        }

        public Int32 DeathCount
        {
            get { return deaths.Count; }
        }
        public Int32 AssistCount;

        public Int32 CreepKills;
        public Int32 CreepDenies;

        public List<Int32> KillStreaks = new List<int>();
        Int32 currentKillStreak = 0;

        public List<Int32> DeathStreaks = new List<int>();
        Int32 currentDeathStreak = 0;

        public Int32 NeutralKills;
        public Int32 TowerKills;
        public Int32 RaxKills;
        public Int32 CourierKills;
        public Int32 CourierDeaths;

        public void KilledBy(String killerName, String time, Kill.KillType killType)
        {
            Kill death = new Kill()
            {
                KillerName = killerName,
                VictimName = Name,
                Type = killType,
                TimeString = time
            };

            deaths.Add(death);
        }

        public void KilledBy(Player killerPlayer, String time, Kill.KillType killType)
        {
            KilledBy(killerPlayer.Name, time, killType);

            switch (killType)
            {
                case Kill.KillType.Hero:
                    if (currentKillStreak > 0 && !KillStreaks.Contains(currentKillStreak))
                        KillStreaks.Add(currentKillStreak);
                    currentKillStreak = 0;
                    currentDeathStreak++;
                    break;
                case Kill.KillType.Tower:
                    break;
                case Kill.KillType.Rax:
                    break;
                case Kill.KillType.Courier:
                    break;
                default:
                    break;
            }

            killerPlayer.Killed(this, time, killType);
        }

        public void Killed(Player victimPlayer, String time, Kill.KillType killType)
        {
            Kill kill = new Kill()
            {
                KillerName = Name,
                VictimName = victimPlayer.Name,
                Type = killType,
                TimeString = time
            };

            kills.Add(kill);

            switch (killType)
            {
                case Kill.KillType.Hero:
                    currentKillStreak++;
                    if (currentDeathStreak > 0 && !DeathStreaks.Contains(currentDeathStreak))
                        DeathStreaks.Add(currentDeathStreak);
                    currentDeathStreak = 0;
                    break;
                case Kill.KillType.Tower:
                    break;
                case Kill.KillType.Rax:
                    break;
                case Kill.KillType.Courier:
                    break;
                default:
                    break;
            }
        }

        public Boolean Quit = false;
        public String QuitAtString = String.Empty;

        public TimeSpan QuitAt
        {
            get
            {
                String temp = QuitAtString.Replace("h", String.Empty).Replace("m", String.Empty).Replace("s", String.Empty);
                TimeSpan t = TimeSpan.Parse(temp);
                return t;
            }
        }

        public List<String> MostFavoredEnemy
        {
            get
            {
                Dictionary<String, Int32> kills = new Dictionary<string, int>();

                IEnumerable<String> killed =
                    from myKills in this.kills
                    select myKills.VictimName;

                foreach (String enemy in killed)
                {
                    if (kills.ContainsKey(enemy))
                        kills[enemy]++;
                    else
                        kills[enemy] = 1;
                }

                List<String> killerData = new List<string>();

                foreach (KeyValuePair<String, Int32> kvp in kills)
                {
                    killerData.Add(
                        String.Format("{0}-{1}", kvp.Value, kvp.Key)
                    );
                }

                return killerData;
            }
        }

        public List<String> LeastFavoredEnemy
        {
            get
            {
                Dictionary<String, Int32> deaths = new Dictionary<string, int>();

                IEnumerable<String> killedBy =
                    from myDeaths in this.deaths
                    select myDeaths.KillerName;

                foreach (String enemy in killedBy)
                {
                    if (deaths.ContainsKey(enemy))
                        deaths[enemy]++;
                    else
                        deaths[enemy] = 1;
                }

                List<String> deathData = new List<string>();

                foreach (KeyValuePair<String, Int32> kvp in deaths)
                {
                    deathData.Add(
                        String.Format("{0}-{1}", kvp.Value, kvp.Key)
                    );
                }

                return deathData;
            }
        }


        public List<String> Items = new List<string>();

        public bool ClearItems()
        {
            int itemCount = Items.Count;

            Items.Clear();

            return (itemCount == 0);
        }

        public String IPAddress = String.Empty;

        static String[] subItems = { "Items", "Heroes" };

        public static Player CreatePlayer(byte[] commandData, Game game)
        {
            Player newPlayer = new Player(game);

            newPlayer.ID = PlayerID(commandData);
            newPlayer.Name = PlayerName(commandData);

            foreach (String s in subItems)
            {
                newPlayer.SubItems.Add(new SubItem(newPlayer) { Name = s });
            }



            return newPlayer;
        }

        public static Player FromJoinRequest(byte[] requestCommandData, byte[] slotInfoJoinCommandData)
        {
            Player newPlayer = new Player();
            newPlayer.Name = requestCommandData.ExtractString(19);
            newPlayer.ID = new byte[] { slotInfoJoinCommandData[slotInfoJoinCommandData.Length - 17], 0 }.ToShort();
            newPlayer.IsSelf = true;
            foreach (String s in subItems)
            {
                newPlayer.SubItems.Add(new SubItem(newPlayer) { Name = s });
            }
            return newPlayer;
        }

        private static Int32 PlayerID(byte[] commandData)
        {

            byte[] byteArray = { 0, commandData[8] };
            int pid = 0;
            if (BitConverter.IsLittleEndian)
                byteArray = byteArray.Reverse().ToArray();

            pid = BitConverter.ToInt16(byteArray, 0);

            return pid;
        }

        private static String PlayerName(byte[] commandData)
        {
            return commandData.ExtractString(9);
        }
    }

    public class Kill
    {
        public enum KillType : byte { Hero, Tower, Rax, Courier }

        String killerName;

        public String KillerName
        {
            get { return killerName; }
            set { killerName = value; }
        }

        String victimName;

        public String VictimName
        {
            get { return victimName; }
            set { victimName = value; }
        }

        String time;

        public String TimeString
        {
            get { return time; }
            set { time = value; }
        }

        public TimeSpan Time
        {
            get
            {
                String temp = time.Replace("h", String.Empty).Replace("m", String.Empty).Replace("s", String.Empty);
                TimeSpan t = TimeSpan.Parse(temp);
                return t;
            }
        }

        KillType type;

        public KillType Type
        {
            get { return type; }
            set { type = value; }
        }

    }

    public class SubItem
    {
        public Player Parent = null;

        string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public SubItem(Player parentPlayer)
        {
            Parent = parentPlayer;
        }

    }

}
